/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package project1;

import java.util.HashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 *
 * @author rr
 */
public class ThreadedSolver {

    private static ThreadedSolver instance = null;
    private int threadCounter = 0;
    
    
    private ThreadedSolver() {
        workers = Executors.newFixedThreadPool(SetUp.getInstance().getThreadCount());
    }

    public static ThreadedSolver getInstance() {
        if (instance == null) {
            instance = new ThreadedSolver();
        }
        return instance;
    }
    
    private int[] solution = null;
    private int n = SetUp.getInstance().getLengthOfSchedule() * SetUp.getInstance().getNumberOfEmployees();

    private ExecutorService workers; 
    
    
    
    public void solve() throws InterruptedException {

        int[] tempAssignment = new int[n];
        for (int i = 0; i < n; i++) {
            tempAssignment[i] = -1;
        }


        boolean[][] allOnes = new boolean[SetUp.getInstance().getNumberOfShifts()+1][SetUp.getInstance().getNumberOfEmployees() * SetUp.getInstance().getLengthOfSchedule()];

        for (int i = 0; i < allOnes.length; ++i) {
            for (int j = 0; j < allOnes[0].length; ++j) {
                allOnes[i][j] = true;
            }
        }

        
        
        
        
        int firstVariable = ConstraintChecker.getInstance().updatePossibleAssignments(allOnes, tempAssignment)[0];
        
        
        for(int i = 0; i<SetUp.getInstance().getNames().length; ++i){
            if(this.solution != null){
                break;
            }
            
            
            if(allOnes[i][firstVariable]){
                CandidateSolution firstTry = new CandidateSolution(tempAssignment, allOnes);
                firstTry.setNextVariableIndex(firstVariable);
                
                TraverseThread thr = new TraverseThread();
                thr.setSol(firstTry);
                workers.execute(thr);
            }
        }
        
        while(true){
            
            Thread.sleep(100);
            if(this.solution != null){
                workers.shutdown();
                break;
            }
        }
    }

    private void traverse(CandidateSolution thisSolution) {
        
        
        int n = SetUp.getInstance().getLengthOfSchedule() * SetUp.getInstance().getNumberOfEmployees();

        for(int i = 0; i<SetUp.getInstance().getNames().length; ++i){
            
            if(this.solution != null){
                break;
            }
            
            if(thisSolution.getPossibleAssignments()[i][thisSolution.getNextVariableIndex()] == true){
                CandidateSolution newSolution = thisSolution.assignNext(i);

                //Run.printWorkSchedule(newSolution.getTempAssignments());
                
                System.out.println("Assigned Variables: "+newSolution.getNumberOfAssignedValues()+" | "+n);
                
                if (newSolution.getNumberOfAssignedValues() == n) {
                    this.solution = newSolution.getTempAssignments();
                    break;
                }                  
                if(newSolution.getNextVariableIndex() != -1){
                    this.traverse(newSolution);
                }
            }
        }
            
        
    }
    
    public int[] getSolution() {
        return solution;
    }

    public void setSolution(int[] solution) {
        this.solution = solution;
    }

    void addThread(TraverseThread newThread) {
        
        this.workers.execute(newThread);
        ++this.threadCounter;
    }

    public ExecutorService getWorkers() {
        return workers;
    }

    public void setWorkers(ExecutorService workers) {
        this.workers = workers;
    }

    public synchronized void decrementThreadCount() {
        --this.threadCounter;
    }

    public synchronized boolean canAddThread() {
        
        return(this.threadCounter < SetUp.getInstance().getThreadCount());
    }
    
    
    
}
